2

前言

Virtual Dom实际上是在浏览器端用JavaScript实现的DOM API,它之与React就类似一个虚拟空间,包括一套整体的virtual DOM模型,生命周期的维护和管理,性能高效的diff算法和将virtual DOM展示为原生DOM的Patch方法。

基于React开发的适合,所有DOM树都是通过virtual DOM构造的,React在virtual DOM上实现了DOM diff算法,当数据更新时,会根据diff算法寻找到变更的DOM节点,并只对变化的部分进行实际的浏览器的DOM更新,而不是重新渲染整个DOM树。

React也可以实现virtual DOM的批处理更新,当操作virtual DOM时,不会马上生成真实DOM,而是会将一个事件循环(event loop)内的两次数据更新进行合并,这样使得React能够在事件循环结束之前完全不用操作真实的DOM。

尽管每次都要构造完整的virtual DOM树,但由于virtual DOM是JavaScript对象,性能极高,而对原生DOM进行操作的仅仅是diff部分,因而能达到提高性能的目的。这样,在保证性能的同时,开发者不再需要关注某个数据的变化如何更新到具体的DOM原生,而只需要关系数据变化的情况下,整个界面是如何渲染的。

以上总结:

  1. Virtual DOM是JavaScript对象,
  2. Virtual DOM包括生命周期管理,diff算法,patch方法;
  3. Virtual DOM可实现批量更新;
  4. Virtual DOM性能搞,原生dom的操作集中在diff部分;

Virtual DOM模型

Virtual DOM之余React类似一个虚拟空间,React的所有工作都是基于Virtual DOM。Virtual DOM拥有一整套Virtual DOM标签,并负责虚拟节点和属性的构建、更新、删除等工作。

构建一套Virtual DOM模型,需要具备一个DOM标签所需的基本元素即可:

  1. 标签名
  1. 节点属性: 包含样式、属性、事件等
  2. 子节点
  3. 标识id

demo:

{
    //标签名
    tagName: 'div',
    //属性
    properties: {
        style: {}
    },
    //子节点
    children: [],
    //唯一标识
    key: 1
}

实际上Virtual DOM不止于此,但是也离不开这些基础元素。接下来深入了解下Virtual的DOM组成吧。

Virtual DOM的中节点称为ReactNode,有三种类型ReactElementReactFragmentReactText

其中ReactElement又分为ReactComponentElement和ReactDOMElement
以下是ReactNode中不同类型节点所需的基础元素:

type ReactNode = ReactElement | ReactFragment | ReacText;
type ReactElement = ReactComponentElement | ReactDOMElement;

type ReactComponentElement<TProps> = {
    type: ReactClass<TProps>,
    props: TProps,
    key: string | boolean | number | null,
    ref: string | null
}

type ReactDOMElement = {
    type:string,
    props: {
        children: ReactNodeList,
        className: string,
        etc.
    },
    key: string | boolean | number | null,
    ref: string
}

type ReactFragment = Array<ReactNode | ReactEmpty>;
type ReactNodeList = ReactNode | ReactEmpty;
type ReactText = string | number;
type ReactEmpty = null | undefined | boolean;

创建React元素

我们都知道JSX语法,现在来回顾下,下面是一段JSX语法编译后的JavaScript:

const Nav, Profile;
//jsx
const app = <Nav color="blue"><Profile>Click</Profile></Nav>

//输出
const app = React.createElement({
    Nav,
    {color: blue},
    React.createElement({
        Profile,
        null,
        "click"
    })
})

通过JSX创建的虚拟元素最终会被编译成调用React的createElement方法,那么createElement方法到底做了什么, 接下来看源码啦!

//createElement 只是做了简单的参数修正, 返回一个ReactElement实例对象。也就是virtual DOM的实例

ReactElement.createElement = function(type, config, children) {
    //初始化参数
    var propName;
    var props = {};
    var key = null;
    var ref = null;
    var self = null;
    var source = null;
    
    //如果存在config, 提取里面的内容
    if(config != null) {
        ref = config.ref === undefined ? null : config.ref
        key = config.key === undefined ? null : ''+config.key
        self = config._self === undefined ? null : config._self
        source = config._source === undefined ? null : config._source   
        
        //复制config里的内容到props(如id和className)
        for(propName in config) {
            if(config.hasOwnProperty(propName)) && !RESERVED_PROPS.hasOwnProperty(propName) {
                props[propName] == config[propName];
            }
        }
    }
    
    //处理children, 全部挂载到props的children属性上,如果只有一个参数,直接赋值给children,否则做合并处理。
    var childrenLength = arguments.length - 2;
    if(childrenLength === 1) {
        props.children = children
    }else if(childrenLength > 1) {
        var childArray = Array(childrenLength);
        for(var i = 0; i < childrenLength; i++ ) {
            childArray[i] = arguments[i + 2]
        }
        props.children = childArray
    }
    
    //如果某个prop为空且存在默认的prop,则将默认prop赋给当前的prop
    if(type && type.defaultProps) {
        var defaultProps = type.defaultProps;
        for(propsName in defaultProps) {
            if(typeof props[propName] === 'undefined') {
                props[propName] = defaultProps[propName]
            }
        }
    }
    
    //返回一个reactElement实例对象
    return ReactElement(type, key, ref, self, source, ReactCurrentOwner.current, props)
}

Virtual DOM模型通过createElement创建虚拟元素,那又是如何创建组件呢?下次再说~~


fsrookie
2.9k 声望256 粉丝

目前很多文章都是摘抄记录其他教程。见谅。